{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true,
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "## 加载 MNIST 数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n",
      "D:\\ProgramData\\Anaconda3\\envs\\tensorflow112\\lib\\site-packages\\tensorflow\\python\\framework\\dtypes.py:523: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n",
      "D:\\ProgramData\\Anaconda3\\envs\\tensorflow112\\lib\\site-packages\\tensorflow\\python\\framework\\dtypes.py:524: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n",
      "D:\\ProgramData\\Anaconda3\\envs\\tensorflow112\\lib\\site-packages\\tensorflow\\python\\framework\\dtypes.py:525: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n",
      "D:\\ProgramData\\Anaconda3\\envs\\tensorflow112\\lib\\site-packages\\tensorflow\\python\\framework\\dtypes.py:526: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n",
      "D:\\ProgramData\\Anaconda3\\envs\\tensorflow112\\lib\\site-packages\\tensorflow\\python\\framework\\dtypes.py:527: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n",
      "D:\\ProgramData\\Anaconda3\\envs\\tensorflow112\\lib\\site-packages\\tensorflow\\python\\framework\\dtypes.py:532: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n"
     ]
    }
   ],
   "source": [
    "from keras.datasets import mnist\n",
    "\n",
    "(x_train, y_train), (x_test, y_test) = mnist.load_data('mnist.npz')"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(60000, 28, 28) <class 'numpy.ndarray'>\n",
      "(60000,) <class 'numpy.ndarray'>\n"
     ]
    }
   ],
   "source": [
    "print(x_train.shape, type(x_train))\n",
    "print(y_train.shape, type(y_train))"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 数据处理：规范化"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(60000, 784) <class 'numpy.ndarray'>\n",
      "(10000, 784) <class 'numpy.ndarray'>\n"
     ]
    }
   ],
   "source": [
    "# 将图像本身从[28,28]转换为[784,]\n",
    "X_train = x_train.reshape(60000, 784)\n",
    "X_test = x_test.reshape(10000, 784)\n",
    "print(X_train.shape, type(X_train))\n",
    "print(X_test.shape, type(X_test))"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "outputs": [],
   "source": [
    "# 将数据类型转换为float32\n",
    "X_train = X_train.astype('float32')\n",
    "X_test = X_test.astype('float32')\n",
    "# 数据归一化\n",
    "X_train /= 255\n",
    "X_test /= 255"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 统计训练数据中各标签数量"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 1 2 3 4 5 6 7 8 9] [5923 6742 5958 6131 5842 5421 5918 6265 5851 5949]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "label, count = np.unique(y_train, return_counts=True)\n",
    "print(label, count)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "outputs": [
    {
     "data": {
      "text/plain": "<Figure size 432x288 with 1 Axes>",
      "image/png": "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U1pBXu87duzgiiuuiN6VMda4ceNo1qwZzZs3p3PnztFxBMlbQd5vAA888ED0UmKAlStX8pe//IWFCxeybNkypk6detwzsxOhYJGH/B7EqlWr8o9//IMVK1YwevRobr311mgZEyZMYNmyZaxcuZLt27fz+uuvJ2VfTsSnn34avRKpQ4cOvPHGG8fN7+64O/v378fd2bNnD7Vq1cpXXdn9182bNycjI4NHH32UyZMnk5qayvvvv0+3bt3o1KkTAPPnz6dFixakp6fTs2dPXnrppejZS69evbj44ov55JNPSE1NZcSIESfwHzg15Pf1XrZsWQYPHsyzzz571PaF/ZIgRzve++3NN9+kXr16NGnSJJq2evVq2rRpE73g4/vf/z6TJk2KS9sULPIpr4N4wQUXRD8MmzRpwtdff83BgwcBoqePWVlZHDp06KgrdoojM6Njx460atWK4cOHA5F9mjJlCgCvv/46mzZtiuZft24dF1xwAd///vej9wMvXbo0w4YNo1mzZtSqVYuPPvooeoVTmOz+6+XLl/Pmm29SuXJlevbsSWZmJgcPHmTr1q28/fbbANx6662sWrWKjIwMli5dSo8ePaLljBs3ji1btnD48GEyMzPzVX9u364zMjJo27ZtNC3n5ZiLFi0iJSWFiRMnRvNnz1vUvHlzXnvttXztd3GU1+u9fPnyXHbZZceMDZ3Il4RkKshxnzNnDmeddVb09z2PPfZYtJzC/LanIO+3ffv28fTTTzNw4MCjymjatCnz5s1jx44dHDhwgGnTph31Hi1KGrPIRfZBNDPuvvtu+vXrFz2IPXr0OOZDM9sbb7xBy5YtOf3006NpnTp1YuHChXTp0oXrrrsukbtRYPPnz6d27dps27aNDh060LBhQ0aOHMnPf/5zBg8ezI9+9CPKlCkDQM2aNdm4cSNVqlRhyZIl9OjRg1WrVnHGGWcwbNgwPvzwQ84991x+9rOf8dRTT/HrX/86tP5k91/Pnj37qLGVhx9+mIEDB9KlSxemTZvGww8/zJw5cwA4cuQIjzzyCB07dozmL1euHK+++ir169fn888/p1WrVnTq1IlKlSoV5e4UucK+3mPFfkkoX7489evX58UXX0zQHpyYghz3yy+/nKlTpx5Txu23307//v257bbb8l1vQd5vgwYN4v7776dChQpHldGoUaPo67B8+fKkp6fHbQqUuJ5ZmFklM5toZh+b2Wozu9jMzjazmWa2JnisHOQ1M/ujma01s+Vm1jKmnN5B/jVm1juebYbIQVy6dCn/+te/ePHFF5k7dy4jR47kpZdeolWrVuzduzd6ELOtWrWKRx55hD//+c9Hpb/99tts2bKFgwcPHjWeURxl/7CpevXq9OzZk4ULF9KwYUNmzJjBkiVL6NWrF+eddx4Ap59+OlWqVAGgVatWnHfeeXz66adkZGQAkfEMM+P666/nP//5T1L250SZWbSvfvfu3Ud9U37hhRe49tprj5qw7vzzz6d+/foA1KpVi+rVq7N9+/bENroQCvN6z+nw4cPRLwmff/45zZs3j04AWNIc77jnpTC/7SnI+23BggU8/PDDpKWl8fzzz/Pkk09Gu/n69u3LkiVLmDt3LpUrV+b8888vUDvyK97dUEOB6e7eEGgBrAZ+Ccxy9/rArOA5QBegfvDXDxgGYGZnAwOBNsBFwMDsABMvBTmIAJmZmfTs2ZNXX331qPRsZcuWpXv37tHTy+Jo//797N27N7o8Y8YMmjZtGp3q+dtvv+Xxxx/nnnvuAWD79u3RAfvPPvuMNWvWcO6551K7dm0++uij6IfkzJkzadSoURL2qGBy6xJ4/vnneeihh6hTpw4PPvhg9MNv8+bNTJ48mZ/85Cd5lrdw4UIOHTqU6+uhuCno6z03JfVLQkGOO8D7779PixYt6NKlC6tWrSp0vQV9v82bN4/169ezfv167rvvPh599NHoBQbZ22zcuJFJkyZx0003FbpdxxO3bigzOwtoB9wO4O6HgENm1h1oH2QbDcwBHgG6A696ZAawD4KzkppB3pnuvjModybQGRgXj3bv37+fb7/9looVK0YP4m9/+1u2bdtG9erVjzmIu3btolu3bgwZMoRLL700Ws6+ffvYu3cvNWvWJCsri3/+859cfvnl8Whykdi6dSs9e/YEImMsN910E507d2bo0KHR7oRrrrmGPn36ADB37lx++9vfUrp0aU477TRefvnl6DergQMH0q5dO0qXLs33vvc9Ro0alZR9KojcugQmTpzIc889x7XXXsuECRPo27cv77zzDvfddx9PP/00p52W+3etLVu2cOuttzJ69Og88xQXBX295yX2S0K1atVKzJeEghz3li1bsmHDBipUqMC0adPo0aMHa9asKVS9BX2/Hc+1117Ljh07KF26NC+++GLcuj0tXrMzmlk6MBz4iMhZxRJgALDZ3SsFeQz4yt0rmdlUYIi7zw/WzSISRNoDZd398SD9N8DX7v5sjvr6ETkjoW7duq02bNhQqHZ/9tlnxxzE//3f/z3mID711FOYGY8//jhPPfVUtPsBIlMpuztXXXUVBw8e5Ntvv+WKK67gueeeC/2VcbL77ZOpuOz7oEGDqFChAoMHD2bXrl2YGe7OWWedxZ49e6hXr150VtMvv/yScuXKMXz4cHr06MGePXto3749jz76aLEfo4KCv94hMii8Z88eDh06RKVKlZgxYwaNGzfm5ZdfZujQoUd9ScjuqsxLcTnmEH7cc0pLS2Px4sXR8Y7169dz1VVXsXLlyhNuf7KY2RJ3z/XHSvEc4E4BWgI/c/cFZjaU77qcAHB3N7MiiVbuPpxIcKJ169aFLvPcc89l2bJlx6QPGDCAAQMGHJP+61//Os/B20WLFhW2GUlRnN64iZTXt+tatWrx3nvv0b59e959993oF4J169ZFt7399tu56qqr6NGjB4cOHaJnz57cdtttJSJQQMFf7xD5UMzNPffcE3oGUpwU9Lh/8cUX1KhRAzNj4cKFfPvtt6HB8HhK2vstnsEiE8h09wXB84lEgsVWM6vp7luCbqbs+x9uBurEbJ8apG3mu26r7PQ5cWx3iTuIcmLy6hKoUKECAwYMICsri7Jly0b7tPMyYcIE5s6dy44dO6Jdb6NGjSI9PT3Oe3BiTtXXe0GP+8SJExk2bBgpKSmcccYZjB8/Pnq21atXL+bMmcOXX35Jamoqv/vd7/J9yXhJEbdg4e5fmNkmM2vg7p8AVxLpkvoI6A0MCR6zR33fAvqb2Xgig9m7g4DyNvBkzKB2R+BX8Wq3nHry+nZ92WWXhc45FDsek9u8RVJ8FfS49+/f/5hfrWcbNy4uQ6jFSrx/Z/EzYKyZlQE+A/oQuQJrgpn1BTYA1wd5pwFdgbXAgSAv7r7TzAYD2X06j2UPdosUlVP12/WpTsc9/+IaLNw9A8htsOTKXPI6cG8e5YwERhZp40REJN+K93V9IiJSLChYiBQDuc1RlO33v/89ZhadxXXs2LE0b96cZs2acckllxzV7677j0u8KFiIFBM5Z34F2LRpEzNmzKBu3brRtHr16vHee++xYsUKfvOb39CvX7/oOt1/XOJFwUKkGLv//vt55plnjpqx+JJLLqFy5cjFgW3btiUzMzO6Tvcfl3hRsBApBnKbo2jKlCnUrl2bFi1a5LndiBEjjroZjki8aIpykWIgtzmKnnzySWbMmJHnNrNnz2bEiBHMnz8/gS2VU5XOLESKgZwzv7733nusW7eOFi1akJaWRmZmJi1btuSLL74AYPny5dx5551MmTLlhKacEMkvBQuRJMttuuoLL7yQbdu2RaelTk1NZenSpZxzzjls3LiRa665hjFjxsTt3gUiOSlYiCTZ1q1bueyyy2jRogUXXXQR3bp1o3Pnznnmf+yxx9ixYwc//elPj7nUVvcfl3jRmIVIkuU1R1Gs2JleX3nlFV555ZVc850KcxRJcujMQkREQunMQiTJNJmdlAQ6sxARkVAKFiIiEkrBQkREQilYiIhIKAULEREJpWAhIiKhFCxERCSUgoWIiISKa7Aws/VmtsLMMsxscZB2tpnNNLM1wWPlIN3M7I9mttbMlptZy5hyegf515hZ73i2WUREjpWIM4sr3D3d3bNnO/slMMvd6wOzgucAXYD6wV8/YBhEggswEGgDXAQMzA4wIiKSGMnohuoOjA6WRwM9YtJf9YgPgEpmVhPoBMx0953u/hUwE8h7Sk4RESly8Q4WDswwsyVmln1X+RruviVY/gKoESzXBjbFbJsZpOWVfhQz62dmi81s8fbt24tyH0RETnnxnkjwMnffbGbVgZlm9nHsSnd3M/OiqMjdhwPDAVq3bl0kZYqISERczyzcfXPwuA2YTGTMYWvQvUTwuC3IvhmoE7N5apCWV7qIiCRI3IKFmZU3s4rZy0BHYCXwFpB9RVNvYEqw/BZwW3BVVFtgd9Bd9TbQ0cwqBwPbHYM0ERFJkHh2Q9UAJptZdj1/d/fpZrYImGBmfYENwPVB/mlAV2AtcADoA+DuO81sMLAoyPeYu++MY7tFRCSHuAULd/8MaJFL+g7gylzSHbg3j7JGAiOLuo0iIpI/+gW3iIiEUrAQEZFQChYiIhJKwUJEREIpWIiISCgFCxERCaVgISIioRQsREQklIKFiIiEUrAQEZFQChYiIhJKwUJEREIpWIiISCgFCxERCaVgISIioRQsREQklIKFiIiEUrAQEZFQ+QoWZnZpftJEROTklN8zixfymSYiIiehlOOtNLOLgUuAamb2QMyqM4FS+anAzEoBi4HN7n6VmdUDxgNVgCXAre5+yMxOB14FWgE7gBvcfX1Qxq+AvsAR4Ofu/nb+d1FERE5U2JlFGaACkaBSMeZvD3BdPusYAKyOef408Jy7/w/wFZEgQPD4VZD+XJAPM2sM3Ag0AToDLwUBSEREEuS4Zxbu/h7wnpmNcvcNBS3czFKBbsATwANmZsAPgJuCLKOBQcAwoHuwDDAR+FOQvzsw3t0PAuvMbC1wEfB+QdsjIiKFc9xgEeN0MxsOpMVu4+4/CNnueeBhImcjEOl62uXuWcHzTKB2sFwb2BSUm2Vmu4P8tYEPYsqM3SbKzPoB/QDq1q2bz90SEZH8yG+weB14GXiFyLhBKDO7Ctjm7kvMrH2hWlcA7j4cGA7QunVrj3d9IiKnkvwGiyx3H1bAsi8FfmRmXYGyRAbFhwKVzCwlOLtIBTYH+TcDdYBMM0sBziIy0J2dni12GxERSYD8Xjr7DzP7qZnVNLOzs/+Ot4G7/8rdU909jcgA9bvufjMwm+8Gx3sDU4Llt4LnBOvfdXcP0m80s9ODK6nqAwvzu4MiInLi8ntmkf0h/lBMmgPnFqLOR4DxZvY48CEwIkgfAYwJBrB3EgkwuPsqM5sAfARkAfe6e766wkREpGjkK1i4e70TqcTd5wBzguXPiFzNlDPPN8CP89j+CSJXVImISBLkK1iY2W25pbv7q0XbHBERKY7y2w11YcxyWeBKYCmRX1yLiMhJLr/dUD+LfW5mlYhM2SEiIqeAwk5Rvh84oXEMEREpOfI7ZvEPIlc/QWQCwUbAhHg1SkREipf8jlk8G7OcBWxw98w4tEdERIqhfHVDBRMKfkxkjqfKwKF4NkpERIqX/N4p73oiv5r+MXA9sMDM8jtFuYiIlHD57Yb6X+BCd98GYGbVgHeITCUuIiInufxeDXVadqAI7CjAtiIiUsLl98xiupm9DYwLnt8ATItPk0REpLgJuwf3/wA13P0hM7sGuCxY9T4wNt6NExGR4iHszOJ54FcA7j4JmARgZs2CdVfHsW0iIlJMhI071HD3FTkTg7S0uLRIRESKnbBgUek4684ownaIiEgxFhYsFpvZXTkTzexOYEl8miQiIsVN2JjFfcBkM7uZ74JDa6AM0DOO7RIRkWLkuMHC3bcCl5jZFUDTIPmf7v5u3FsmIiLFRn7vZzEbmB3ntoiISDGlX2GLiEiouAULMytrZgvNbJmZrTKz3wXp9cxsgZmtNbPXzKxMkH568HxtsD4tpqxfBemfmFmneLVZRERyF88zi4PAD9y9BZAOdDaztsDTwHPu/j/AV0DfIH9f4Ksg/bkgH2bWGLgRaAJ0Bl4ys1JxbLeIiOQQt2DhEfuCp6WDPwd+wHez1Y4GegTL3YPnBOuvNDML0se7+0F3XwesBS6KV7tFRORYcR2zMLNSZpYBbANmAv8Fdrl7VpAlE6gdLNcGNgEE63cDVWLTc9kmtq5+ZrbYzBZv3749DnsjInLqimuwcPcj7p4OpBI5G2gYx7qGu3trd29drVq1eFUjInJKSsjVUO6+i8iltxcDlcws+5LdVGBzsLwZqAMQrD+LyH0zoum5bCMiIgkQz6uhqplZpWD5DKADsJpI0Mi+JWtvYEqw/FbwnGD9u+7uQfqNwdVS9YD6RG7xKiIiCZLfmx8VRk1gdHDl0mnABHefamYfAePN7HHgQ2BEkH8EMMbM1gI7iVwBhbuvMrMJwEdAFnCvux+JY7tFRCSHuAULd18OXJBL+mfkcjWTu38D/DiPsp4AnijqNoqISP7oF9wiIhJKwUJEREIpWIiISCgFCxERCaVgISIioRQsREQklIKFiIiEUrAQEZFQChYiIhJKwUJEREIpWIiISCgFCxERCaVgISIioRQsREQklIKFiIiEUrAQEZFQChYiIhJKwUJEREIpWIiISCgFCxERCRW3YGFmdcxstpl9ZGarzGxAkH62mc00szXBY+Ug3czsj2a21syWm1nLmLJ6B/nXmFnveLVZRERyF88ziyzgF+7eGGgL3GtmjYFfArPcvT4wK3gO0AWoH/z1A4ZBJLgAA4E2wEXAwOwAIyIiiRG3YOHuW9x9abC8F1gN1Aa6A6ODbKOBHsFyd+BVj/gAqGRmNYFOwEx33+nuXwEzgc7xareIiBwrIWMWZpYGXAAsAGq4+5Zg1RdAjWC5NrApZrPMIC2v9Jx19DOzxWa2ePv27UW7AyIip7i4BwszqwC8Adzn7nti17m7A14U9bj7cHdv7e6tq1WrVhRFiohIIK7BwsxKEwkUY919UpC8NeheInjcFqRvBurEbJ4apOWVLiIiCRLPq6EMGAGsdvc/xKx6C8i+oqk3MCUm/bbgqqi2wO6gu+ptoKOZVQ4GtjsGaSIikiApcSz7UuBWYIWZZQRpjwJDgAlm1hfYAFwfrJsGdAXWAgeAPgDuvtPMBgOLgnyPufvOOLZbRERyiFuwcPf5gOWx+spc8jtwbx5ljQRGFl3rRESkIPQLbhERCaVgISIioRQsREQklIKFiIiEUrAQEZFQChYiIhJKwUJEREIpWIiISCgFCxERCaVgISIioRQsREQklIKFiIiEUrAQEZFQChYiIhJKwUJEREIpWIiISCgFCxERCaVgISIioRQsREQklIKFiIiEiluwMLORZrbNzFbGpJ1tZjPNbE3wWDlINzP7o5mtNbPlZtYyZpveQf41ZtY7Xu0VEZG8xfPMYhTQOUfaL4FZ7l4fmBU8B+gC1A/++gHDIBJcgIFAG+AiYGB2gBERkcSJW7Bw97nAzhzJ3YHRwfJooEdM+qse8QFQycxqAp2Ame6+092/AmZybAASEZE4S/SYRQ133xIsfwHUCJZrA5ti8mUGaXmlH8PM+pnZYjNbvH379qJttYjIKS5pA9zu7oAXYXnD3b21u7euVq1aURUrIiIkPlhsDbqXCB63BembgTox+VKDtLzSRUQkgRIdLN4Csq9o6g1MiUm/Lbgqqi2wO+iuehvoaGaVg4HtjkGaiIgkUEq8CjazcUB7oKqZZRK5qmkIMMHM+gIbgOuD7NOArsBa4ADQB8Ddd5rZYGBRkO8xd885aC4iInEWt2Dh7r3yWHVlLnkduDePckYCI4uwaSIiUkD6BbeIiIRSsBARkVAKFiIiEkrBQkREQilYiIhIKAULEREJpWAhIiKhFCxERCSUgoWIiIRSsBARkVAKFiIiEkrBQkREQilYiIhIKAULEREJpWAhIiKhFCxERCSUgoWIiIRSsBARkVAKFiIiEkrBQkREQpWYYGFmnc3sEzNba2a/THZ7REROJSUiWJhZKeBFoAvQGOhlZo2T2yoRkVNHiQgWwEXAWnf/zN0PAeOB7kluk4jIKcPcPdltCGVm1wGd3f3O4PmtQBt37x+Tpx/QL3jaAPgkQc2rCnyZoLpUd/Go/1StO9n1a9/j73vuXi23FSkJqDwh3H04MDzR9ZrZYndvneh6T+W6k13/qVp3suvXvidv36HkdENtBurEPE8N0kREJAFKSrBYBNQ3s3pmVga4EXgryW0SETlllIhuKHfPMrP+wNtAKWCku69KcrOyJbzrS3Unvf5Tte5k1699T6ISMcAtIiLJVVK6oUREJIkULEREJJSCRSElc/oRMxtpZtvMbGUi6w3qrmNms83sIzNbZWYDElh3WTNbaGbLgrp/l6i6Y9pQysw+NLOpSah7vZmtMLMMM1uc4LormdlEM/vYzFab2cUJrLtBsM/Zf3vM7L4E1n9/8HpbaWbjzKxsAuseENS7KpH7nGtbNGZRcMH0I58CHYBMIldr9XL3jxJUfztgH/CquzdNRJ0xddcEarr7UjOrCCwBeiRi383MgPLuvs/MSgPzgQHu/kG8645pwwNAa+BMd78qUfUGda8HWrt7wn8YZmajgXnu/kpwRWI5d9+VhHaUInLZfBt335CA+moTeZ01dvevzWwCMM3dRyWg7qZEZqu4CDgETAfucfe18a47NzqzKJykTj/i7nOBnYmqL0fdW9x9abC8F1gN1E5Q3e7u+4KnpYO/hH3bMbNUoBvwSqLqLA7M7CygHTACwN0PJSNQBK4E/puIQBEjBTjDzFKAcsDnCaq3EbDA3Q+4exbwHnBNguo+hoJF4dQGNsU8zyRBH5jFiZmlARcACxJYZykzywC2ATPdPWF1A88DDwPfJrDOWA7MMLMlwfQ2iVIP2A78NeiCe8XMyiew/lg3AuMSVZm7bwaeBTYCW4Dd7j4jQdWvBC43sypmVg7oytE/Tk4oBQspFDOrALwB3OfuexJVr7sfcfd0Ir/ivyg4VY87M7sK2ObuSxJRXx4uc/eWRGZfvjfojkyEFKAlMMzdLwD2Awm/TUDQ/fUj4PUE1lmZSK9BPaAWUN7MbklE3e6+GngamEGkCyoDOJKIunOjYFE4p/T0I8F4wRvAWHeflIw2BN0gs4HOCaryUuBHwbjBeOAHZva3BNUNRL/l4u7bgMlEukMTIRPIjDmLm0gkeCRaF2Cpu29NYJ0/BNa5+3Z3PwxMAi5JVOXuPsLdW7l7O+ArImOlSaFgUTin7PQjwSDzCGC1u/8hwXVXM7NKwfIZRC4w+DgRdbv7r9w91d3TiBzvd909Id8wAcysfHBBAUEXUEci3RRx5+5fAJvMrEGQdCWQkIs5cuhFArugAhuBtmZWLnjtX0lknC4hzKx68FiXyHjF3xNVd04lYrqP4ibZ04+Y2TigPVDVzDKBge4+IkHVXwrcCqwIxg4AHnX3aQmouyYwOrgi5jRggrsn/BLWJKkBTI58XpEC/N3dpyew/p8BY4MvR58BfRJYd3aA7ADcnch63X2BmU0ElgJZwIckduqNN8ysCnAYuDeJFxbo0lkREQmnbigREQmlYCEiIqEULEREJJSChYiIhFKwEBGRUAoWIifAzPaF54rmHWRmD8arfJF4UrAQEZFQChYiRczMrjazBcGke++YWY2Y1S3M7H0zW2Nmd8Vs85CZLTKz5cm4T4dIGAULkaI3H2gbTLo3nshMtdmaAz8ALgZ+a2a1zKwjUJ/IXE/pQKsEThIoki+a7kOk6KUCrwU3iioDrItZN8Xdvwa+NrPZRALEZUTmevowyFOBSPCYm7gmixyfgoVI0XsB+IO7v2Vm7YFBMetyzq/jgAFPufufE9I6kUJQN5RI0TuL76as751jXffgXuJViEwGuYjIhJR3BPcIwcxqZ882KlJc6MxC5MSUC2b+zfYHImcSr5vZV8C7RG6ck205kftwVAUGu/vnwOdm1gh4P5hVdh9wC5G7AYoUC5p1VkREQqkbSkREQilYiIhIKAULEREJpWAhIiKhFCxERCSUgoWIiIRSsBARkVD/Dy09830v1xg+AAAAAElFTkSuQmCC\n"
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure()\n",
    "plt.bar(label, count, width = 0.7, align='center')\n",
    "plt.title(\"Label Distribution\")\n",
    "plt.xlabel(\"Label\")\n",
    "plt.ylabel(\"Count\")\n",
    "plt.xticks(label)\n",
    "plt.ylim(0,7500)\n",
    "\n",
    "for a,b in zip(label, count):\n",
    "    plt.text(a, b, '%d' % b, ha='center', va='bottom',fontsize=10)\n",
    "\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 数据处理：one-hot 编码\n",
    "### 几种编码方式的对比\n",
    "\n",
    "| Binary | Gray code | One-hot |\n",
    "| ------ | ------ | ------ |\n",
    "|000|000|00000001|\n",
    "|001|001|00000010|\n",
    "|010|011|00000100|\n",
    "|011|010|00001000|\n",
    "|100|110|00010000|\n",
    "|101|111|00100000|\n",
    "|110|101|01000000|\n",
    "|111|100|10000000|\n",
    "\n",
    "### one-hot 应用\n",
    "\n",
    "![one-hot 应用.png](https://ws1.sinaimg.cn/large/affaa66dgy1gl8nflq7bmj21fq0im40a.jpg)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Shape before one-hot encoding:  (60000,)\n",
      "Shape after one-hot encoding:  (60000, 10)\n"
     ]
    }
   ],
   "source": [
    "from keras.utils import np_utils\n",
    "\n",
    "n_classes = 10\n",
    "print(\"Shape before one-hot encoding: \", y_train.shape)\n",
    "Y_train = np_utils.to_categorical(y_train, n_classes)\n",
    "print(\"Shape after one-hot encoding: \", Y_train.shape)\n",
    "Y_test = np_utils.to_categorical(y_test, n_classes)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]\n"
     ]
    }
   ],
   "source": [
    "print(y_train[3])\n",
    "print(Y_train[3])"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 使用 Keras sequential model 定义神经网络\n",
    "### softmax 网络层\n",
    "\n",
    "![softmax 网络层.jpg](https://ws1.sinaimg.cn/large/affaa66dgy1gl8nul0l8dj21840vgjy4.jpg)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "outputs": [],
   "source": [
    "from keras.models import Sequential\n",
    "from keras.layers.core import Dense, Activation\n",
    "\n",
    "model = Sequential()\n",
    "model.add(Dense(512, input_shape=(784,)))\n",
    "model.add(Activation('relu'))\n",
    "\n",
    "model.add(Dense(512))\n",
    "model.add(Activation('relu'))\n",
    "\n",
    "model.add(Dense(10))\n",
    "model.add(Activation('softmax'))"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 编译模型\n",
    "\n",
    "[model.compile()](https://keras.io/models/sequential/#compile)\n",
    "\n",
    "```python\n",
    "compile(optimizer, loss=None, metrics=None, loss_weights=None, sample_weight_mode=None, weighted_metrics=None, target_tensors=None)\n",
    "```"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "outputs": [],
   "source": [
    "model.compile(loss='categorical_crossentropy', metrics=['accuracy'], optimizer='adam')"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 训练模型，并将指标保存到 history 中\n",
    "\n",
    "[model.fit()](https://keras.io/models/sequential/#fit)\n",
    "\n",
    "```python\n",
    "fit(x=None, y=None, batch_size=None, epochs=1, verbose=1, callbacks=None, validation_split=0.0, validation_data=None, shuffle=True, class_weight=None, sample_weight=None, initial_epoch=0, steps_per_epoch=None, validation_steps=None)\n",
    "```"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 60000 samples, validate on 10000 samples\n",
      "Epoch 1/5\n",
      " - 4s - loss: 0.2214 - acc: 0.9326 - val_loss: 0.1070 - val_acc: 0.9651\n",
      "Epoch 2/5\n",
      " - 4s - loss: 0.0766 - acc: 0.9763 - val_loss: 0.0740 - val_acc: 0.9766\n",
      "Epoch 3/5\n",
      " - 4s - loss: 0.0486 - acc: 0.9843 - val_loss: 0.0721 - val_acc: 0.9786\n",
      "Epoch 4/5\n",
      " - 4s - loss: 0.0330 - acc: 0.9895 - val_loss: 0.0690 - val_acc: 0.9792\n",
      "Epoch 5/5\n",
      " - 4s - loss: 0.0267 - acc: 0.9911 - val_loss: 0.0719 - val_acc: 0.9783\n"
     ]
    }
   ],
   "source": [
    "history = model.fit(X_train,\n",
    "                    Y_train,\n",
    "                    batch_size=128,\n",
    "                    epochs=5,\n",
    "                    verbose=2,\n",
    "                    validation_data=(X_test, Y_test))"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 可视化指标"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "outputs": [
    {
     "data": {
      "text/plain": "<Figure size 432x288 with 2 Axes>",
      "image/png": "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\n"
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure()\n",
    "plt.subplot(2,1,1)\n",
    "plt.plot(history.history['acc'])\n",
    "plt.plot(history.history['val_acc'])\n",
    "plt.title('Model Accuracy')\n",
    "plt.ylabel('accuracy')\n",
    "plt.xlabel('epoch')\n",
    "plt.legend(['train', 'test'], loc='lower right')\n",
    "\n",
    "plt.subplot(2,1,2)\n",
    "plt.plot(history.history['loss'])\n",
    "plt.plot(history.history['val_loss'])\n",
    "plt.title('Model Loss')\n",
    "plt.ylabel('loss')\n",
    "plt.xlabel('epoch')\n",
    "plt.legend(['train', 'test'], loc='upper right')\n",
    "plt.tight_layout()\n",
    "\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 保存模型\n",
    "\n",
    "[model.save()](https://keras.io/getting-started/faq/#how-can-i-save-a-keras-model)\n",
    "\n",
    "You can use `model.save(filepath)` to save a Keras model into a single **HDF5 file** which will contain:\n",
    "\n",
    "- the architecture of the model, allowing to re-create the model\n",
    "- the weights of the model\n",
    "- the training configuration (loss, optimizer)\n",
    "- the state of the optimizer, allowing to resume training exactly where you left off.\n",
    "\n",
    "You can then use `keras.models.load_model(filepath)` to reinstantiate your model. load_model will also take care of compiling the model using the saved training configuration (unless the model was never compiled in the first place)."
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Saved trained model at ./mnist/model/keras_mnist.h5 \n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import tensorflow.gfile as gfile\n",
    "\n",
    "save_dir = \"./mnist/model/\"\n",
    "\n",
    "if gfile.Exists(save_dir):\n",
    "    gfile.DeleteRecursively(save_dir)\n",
    "gfile.MakeDirs(save_dir)\n",
    "\n",
    "model_name = 'keras_mnist.h5'\n",
    "model_path = os.path.join(save_dir, model_name)\n",
    "model.save(model_path)\n",
    "print('Saved trained model at %s ' % model_path)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 加载模型"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'str' object has no attribute 'decode'",
     "output_type": "error",
     "traceback": [
      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[1;31mAttributeError\u001B[0m                            Traceback (most recent call last)",
      "\u001B[1;32m<ipython-input-20-74b8fafda702>\u001B[0m in \u001B[0;36m<module>\u001B[1;34m\u001B[0m\n\u001B[0;32m      1\u001B[0m \u001B[1;32mfrom\u001B[0m \u001B[0mkeras\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmodels\u001B[0m \u001B[1;32mimport\u001B[0m \u001B[0mload_model\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m      2\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 3\u001B[1;33m \u001B[0mmnist_model\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mload_model\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel_path\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m",
      "\u001B[1;32mD:\\ProgramData\\Anaconda3\\envs\\tensorflow112\\lib\\site-packages\\keras\\engine\\saving.py\u001B[0m in \u001B[0;36mload_model\u001B[1;34m(filepath, custom_objects, compile)\u001B[0m\n\u001B[0;32m    417\u001B[0m     \u001B[0mf\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mh5dict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfilepath\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'r'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m    418\u001B[0m     \u001B[1;32mtry\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 419\u001B[1;33m         \u001B[0mmodel\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0m_deserialize_model\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mf\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcustom_objects\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcompile\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m    420\u001B[0m     \u001B[1;32mfinally\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m    421\u001B[0m         \u001B[1;32mif\u001B[0m \u001B[0mopened_new_file\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n",
      "\u001B[1;32mD:\\ProgramData\\Anaconda3\\envs\\tensorflow112\\lib\\site-packages\\keras\\engine\\saving.py\u001B[0m in \u001B[0;36m_deserialize_model\u001B[1;34m(f, custom_objects, compile)\u001B[0m\n\u001B[0;32m    222\u001B[0m     \u001B[1;32mif\u001B[0m \u001B[0mmodel_config\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m    223\u001B[0m         \u001B[1;32mraise\u001B[0m \u001B[0mValueError\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;34m'No model found in config.'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 224\u001B[1;33m     \u001B[0mmodel_config\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mjson\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mloads\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel_config\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdecode\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;34m'utf-8'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m    225\u001B[0m     \u001B[0mmodel\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mmodel_from_config\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel_config\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcustom_objects\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mcustom_objects\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m    226\u001B[0m     \u001B[0mmodel_weights_group\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mf\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;34m'model_weights'\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n",
      "\u001B[1;31mAttributeError\u001B[0m: 'str' object has no attribute 'decode'"
     ]
    }
   ],
   "source": [
    "from keras.models import load_model\n",
    "\n",
    "mnist_model = load_model(model_path)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'mnist_model' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[1;31mNameError\u001B[0m                                 Traceback (most recent call last)",
      "\u001B[1;32m<ipython-input-21-3c97acb4420a>\u001B[0m in \u001B[0;36m<module>\u001B[1;34m\u001B[0m\n\u001B[1;32m----> 1\u001B[1;33m \u001B[0mloss_and_metrics\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mmnist_model\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mevaluate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX_test\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mY_test\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mverbose\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;36m2\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m      2\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m      3\u001B[0m \u001B[0mprint\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;34m\"Test Loss: {}\"\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mformat\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mloss_and_metrics\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m      4\u001B[0m \u001B[0mprint\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;34m\"Test Accuracy: {}%\"\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mformat\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mloss_and_metrics\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m*\u001B[0m\u001B[1;36m100\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m      5\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n",
      "\u001B[1;31mNameError\u001B[0m: name 'mnist_model' is not defined"
     ]
    }
   ],
   "source": [
    "loss_and_metrics = mnist_model.evaluate(X_test, Y_test, verbose=2)\n",
    "\n",
    "print(\"Test Loss: {}\".format(loss_and_metrics[0]))\n",
    "print(\"Test Accuracy: {}%\".format(loss_and_metrics[1]*100))\n",
    "\n",
    "predicted_classes = mnist_model.predict_classes(X_test)\n",
    "\n",
    "correct_indices = np.nonzero(predicted_classes == y_test)[0]\n",
    "incorrect_indices = np.nonzero(predicted_classes != y_test)[0]\n",
    "print(\"Classified correctly count: {}\".format(len(correct_indices)))\n",
    "print(\"Classified incorrectly count: {}\".format(len(incorrect_indices)))"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "name": "tensorflow112",
   "language": "python",
   "display_name": "tensorflow112"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}